/*
 * XBinding.java
 * 
 * Copyright (c) 2006 - 2007
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.xfc.editor;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import org.xfc.util.XJavaBean;
import org.xfc.util.XUtils;
import org.xfc.util.reflect.XReflectObject;

// TODO: use different strategy classes for setters, getters, and add / remove property change listeners

// TODO: also handle type conversions

// TODO: also handle buffering values - editor values buffered before being flushed into the object.



/**
 * A class representing a binding between an object's property and an XEditor.
 * 
 * @author Devon Carew
 */
public class XBinding
{
	private Object 			sourceObject;
	private String 			property;
	private XEditor			editor;
	
	private XReflectObject 	reflectObject;
	
	/** True if we are currently processing an editor or property change event. */
	private boolean			inEvent;
	
	private PropertyChangeListener propertyListener = new PropertyChangeListener() {
		public void propertyChange(PropertyChangeEvent event) {
			propertyValueChanged(event);
		}
	};
	
	private XEditorListener editorListener = new XEditorListener() {
		public void valueChanged(XEditorEvent event) {
			editorValueChanged(event);
		}
	};
	
	
	public XBinding(Object sourceObject, String property, XEditor editor)
	{
		this.sourceObject = sourceObject;
		this.property = property;
		this.editor = editor;
		
		reflectObject = XReflectObject.createReflectObject(sourceObject);
		
		// Listen to changes on the editor, change the source object's property.
		editor.addEditorListener(editorListener);
		
		// Listen to property change events on the source object, notify the editor that the value changed.
		if (sourceObject instanceof XJavaBean)
		{
			((XJavaBean)sourceObject).addPropertyChangeListener(propertyListener);
		}
		else
		{
			try
			{
				reflectObject.invoke("addPropertyChangeListener", propertyListener);
			}
			catch (RuntimeException re)
			{
				// Unable to add a listener to property change events.
				
				propertyListener = null;
			}
		}
	}
	
	public void unbind()
	{
		// Remove the editor listener.
		editor.removeEditorListener(editorListener);
		
		// Remove the property change listener.
		if (propertyListener != null)
		{
			if (sourceObject instanceof XJavaBean)
			{
				((XJavaBean)sourceObject).removePropertyChangeListener(propertyListener);
			}
			else
			{
				try
				{
					reflectObject.invoke("removePropertyChangeListener", propertyListener);
				}
				catch (RuntimeException re)
				{
					
				}
			}
		}
	}
	
	public boolean isListeningToEditorEvents()
	{
		return editorListener != null;
	}
	
	public boolean isListeningToPropertyChangeEvents()
	{
		return propertyListener != null;
	}
	
	public XEditor getEditor()
	{
		return editor;
	}
	
	public Object getSourceObject()
	{
		return sourceObject;
	}
	
	public String getProperty()
	{
		return property;
	}
	
	private void propertyValueChanged(PropertyChangeEvent event)
	{
		if (inEvent)
			return;
		
		try
		{
			inEvent = true;
			
			if (property.equals(event.getPropertyName()))
			{
				// Update the editor.
				
				editor.setValue(event.getNewValue());
				editor.setModified(false);
			}
		}
		finally
		{
			inEvent = false;
		}
	}
	
	private void editorValueChanged(XEditorEvent event)
	{
		if (inEvent)
			return;
		
		try
		{
			inEvent = true;
			
			setPropertyValue(event.getNewValue());
		}
		finally
		{
			inEvent = false;
		}
	}
	
	private void setPropertyValue(Object value)
	{
		// TODO: update the object's property -
		
		try
		{
			String methodName = "set" + XUtils.capitalize(property);
			
			reflectObject.invoke(methodName, value);
			
			return;
		}
		catch (RuntimeException re)
		{
			
		}
		
		// Try to set the field directly.
		try
		{
			reflectObject.setField(property, value);
			
			return;
		}
		catch (RuntimeException re)
		{
			// TODO: hmmm -
			
		}
	}
	
}
